前后端使用利用WebSocket进行通信

您所在的位置:网站首页 后端 服务端 前后端使用利用WebSocket进行通信

前后端使用利用WebSocket进行通信

2024-07-16 01:49| 来源: 网络整理| 查看: 265

前后端使用利用WebSocket进行通信 1、前后端如何连接

在这里插入图片描述

服务端利用SpringBoot启动一个WebSocket服务,同时暴露出该服务的应用路径,客户端则利用该应用路径进行连接。需要注意的是,在服务端只需要启动一个WebSocket服务,而每一个客户端就是一个WebSocket应用。

在这里插入图片描述

就有点像:服务端是古老的电话接线员,而客户端就是打电话的人。假如用户A想要给用户B打电话就需要先打电话到接线员那,然后接线员再接通用户B。不过WebSocket可以实现广播和私聊。

2、前端初始化WebSocket对象

不需要引入第三方依赖包,直接使用js自带的WebSocket对象。

创建WebSocket对象

const ws = new WebSocket('ws://localhost:8000/websocket/')

ws://和jdbc://、http://一样都是协议名,同样的,Websocket还支持更加安全的wss://,/websocket即该服务的应用路径名

onopen事件监听

与服务端连接成功会触发。

webSocketOnOpen(e){ console.log('与服务端连接打开->',e) },

onerror事件监听

与服务端连接异常时触发。

webSocketOnError(e){ console.log('与服务端连接异常->',e) },

onclose事件监听

与服务端连接关闭时触发。

webSocketOnClose(e){ console.log('与服务端连接关闭->',e) },

onmessage事件监听

接收到来自服务端的消息时触发。

webSocketOnMessage(e){ console.log('来自服务端的消息->',e) },

一个完整的WebSocket对象应该具备以上属性,同时需要将以上属性跟WebSocket对象绑定。

使用原生JS初始化WebSocket对象演示 const ws = new WebSocket(webSocketUrl) //onopen事件监听 ws.addEventListener('open',e=>{ console.log('与服务端连接打开->',e) },false) //onclose事件监听 ws.addEventListener('close',e=>{ console.log('与服务端连接关闭->',e) },false) //onmessage事件监听 ws.addEventListener('message',e=>{ console.log('来自服务端的消息->',e) },false) //onerror事件监听 ws.addEventListener('error',e=>{ console.log('与服务端连接异常->',e) },false)

ws对象的addEventListener( )方法,为WebSocket绑定事件监听,从而在各个事件监听中处理事务。

使用Vue初始化WebSocket对象演示 export default { name: "Home", data() { return { webSocketObject: null, } }, created() { //初始化WebSocket this.webSocketInit() }, methods: { webSocketInit(){ const webSocketUrl = 'ws://localhost:8000/websocket/'+this.username this.webSocketObject = new WebSocket(webSocketUrl); this.webSocketObject.onopen = this.webSocketOnOpen this.webSocketObject.onmessage = this.webSocketOnMessage this.webSocketObject.onerror = this.webSocketOnError this.webSocketObject.onclose = this.webSocketOnClose }, webSocketOnOpen(e){ console.log('与服务端连接打开->',e) }, webSocketOnMessage(e){ console.log('来自服务端的消息->',e) }, webSocketOnError(e){ console.log('与服务端连接异常->',e) }, webSocketOnClose(e){ console.log('与服务端连接关闭->',e) }, }, }

同样的,利用methods分别定义好OnOpen、OnMessage、OnError、OnClose四个事件监听,然后进行初始化并且绑定就可以了。

这样就完成了WebSocket对象以及事件监听的初始化。

3、后端初始化WebSocket对象

SpringBoot自带的WebSocket有以下5个注解需要注意:

@ServerEndpoint

暴露出的ws应用的路径,支持RESTful风格传参,类似/websocket/{username}

@OnOpen

与当前客户端连接成功,有入参Session对象(当前连接对象),同时可以利用@PathParam()获取上述应用路径中传递的参数,比如@PathParam("username") String username。

@OnClose

与当前客户端连接失败,有入参Session对象(当前连接对象),同时也可以利用@PathParam()获取上述应用路径中传递的参数。

@OnError

与当前客户端连接异常,有入参Session对象(当前连接对象)、Throwable对象(异常对象),同时也可以利用@PathParam()获取上述应用路径中传递的参数。

@OnMessage

当前客户端发送消息,有入参Session对象(当前连接对象)、String message对象(当前客户端传递过来的字符串消息)

利用SpringBoot创建项目,需要引入依赖:

org.springframework.boot spring-boot-starter-websocket

在application.yaml中定义好该服务的端口号:

server: port: 8000

利用自定义配置类开启WebSocket:

package cn.wqk.serverwebsocket.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.server.standard.ServerEndpointExporter; @Configuration @EnableWebSocket public class WebSocketConfig { @Bean public ServerEndpointExporter serverEndpointExporter(){ return new ServerEndpointExporter(); } }

定义Websocket主业务类:

package cn.wqk.serverwebsocket.socket; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import javax.websocket.*; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.Date; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicInteger; @Component @Slf4j @ServerEndpoint("/websocket/{username}") //暴露的ws应用的路径 public class WebSocket { /** * 客户端与服务端连接成功 * @param session * @param username */ @OnOpen public void onOpen(Session session,@PathParam("username") String username){ /* do something for onOpen 与当前客户端连接成功时 */ } /** * 客户端与服务端连接关闭 * @param session * @param username */ @OnClose public void onClose(Session session,@PathParam("username") String username){ /* do something for onClose 与当前客户端连接关闭时 */ } /** * 客户端与服务端连接异常 * @param error * @param session * @param username */ @OnError public void onError(Throwable error,Session session,@PathParam("username") String username) { } /** * 客户端向服务端发送消息 * @param message * @param username * @throws IOException */ @OnMessage public void onMsg(Session session,String message,@PathParam("username") String username) throws IOException { /* do something for onMessage 收到来自当前客户端的消息时 */ } } 4、前后端联动实现简单聊天室

前端在OnMessage事件监听中接收到来自后端的消息,然后进行处理(展示在页面上);同样的,后端也是在OnMessage中接收到来自前端的消息,然后进行处理(发送到所有客户端)。

前端

定义一个输入框,再定义一个按钮:接收消息并且发送

发送 handleSendButton() { const username = this.username const message = this.sendMessage this.webSocketObject.send(JSON.stringify({ id: 1, message, username, time: new Date().getTime() })) this.sendMessage = '' },

**注意:**直接利用websocket对象的send()方法发送消息,前后端数据传输利用JSON字符串,所以发送的时候需要将对象转为JSON字符串。

定义一个列表:用于展示聊天信息

消息编号 发送者 发送时间 发送内容 {{ item.id }} {{ item.username }} {{ new Date(item.time).toLocaleTimeString() }} {{ item.message }}

当客户端的onMessage接收到消息后就把消息展示到列表中:

webSocketOnMessage(e){ console.log('来自服务端的消息->',e) const receiveMessage = JSON.parse(e.data); this.messageList.push(receiveMessage) },

**注意:**通过console.log(e)不难发现,来自服务端的消息是存储在e.data中的,并且是JSON字符串,所以我们需要将它转为JSON对象。

此时已经完成了前端发送消息并且接收消息且展示消息了。

后端

接收消息并且群发消息:

@OnMessage public void onMsg(Session session,String message,@PathParam("username") String username) throws IOException { /* do something for onMessage 收到来自当前客户端的消息时 */ sendAllMessage(message); } //向所有客户端发送消息(广播) private void sendAllMessage(String message){ Set sessionIdSet = onlineClientMap.keySet(); //获得Map的Key的集合 for (String sessionId : sessionIdSet) { //迭代Key集合 Session session = onlineClientMap.get(sessionId); //根据Key得到value session.getAsyncRemote().sendText(message); //发送消息给客户端 } } 完整代码 消息编号 发送者 发送时间 发送内容 {{ item.id }} {{ item.username }} {{ new Date(item.time).toLocaleTimeString() }} {{ item.message }} 发送 退出 import { getUsername, removeUsername } from "@/utils/username"; export default { name: "Home", data() { return { webSocketObject: null, username: '', messageList: [ ], sendMessage: '' } }, created() { //从localStorage中获得username this.username = getUsername() //如果username不存在返回到登录页面 if (!this.username){ this.$router.push({ name: 'Login' }) } //初始化WebSocket this.webSocketInit() }, beforeDestroy() { this.webSocketObject.close();//在该组件销毁时关闭该连接以节约资源 }, methods: { webSocketInit(){ const webSocketUrl = 'ws://localhost:8000/websocket/'+this.username this.webSocketObject = new WebSocket(webSocketUrl); this.webSocketObject.onopen = this.webSocketOnOpen this.webSocketObject.onmessage = this.webSocketOnMessage this.webSocketObject.onerror = this.webSocketOnError this.webSocketObject.onclose = this.webSocketOnClose }, webSocketOnOpen(e){ console.log('与服务端连接打开->',e) }, webSocketOnMessage(e){ console.log('来自服务端的消息->',e) const receiveMessage = JSON.parse(e.data); this.messageList.push(receiveMessage) }, webSocketOnError(e){ console.log('与服务端连接异常->',e) }, webSocketOnClose(e){ console.log('与服务端连接关闭->',e) }, handleSendButton() { const username = this.username const message = this.sendMessage this.webSocketObject.send(JSON.stringify({ id: 1, message, username, time: new Date().getTime() })) this.sendMessage = '' }, handleLogoutButton(){ removeUsername() //清除username然后断开连接 this.webSocketObject.close(); this.$router.push({ name: 'Login' }) } }, }

Tips:

我这里采用的是在上一个页面获取到用户的用户名然后存储到LocalStorage中。

package cn.wqk.serverwebsocket.socket; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Component; import javax.websocket.*; import javax.websocket.server.PathParam; import javax.websocket.server.ServerEndpoint; import java.io.IOException; import java.util.Date; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; import java.util.concurrent.CopyOnWriteArraySet; import java.util.concurrent.atomic.AtomicInteger; @Component @Slf4j @ServerEndpoint("/websocket/{username}") //暴露的ws应用的路径 public class WebSocket { /** 当前在线客户端数量(线程安全的) */ private static AtomicInteger onlineClientNumber = new AtomicInteger(0); /** 当前在线客户端集合(线程安全的):以键值对方式存储,key是连接的编号,value是连接的对象 */ private static Map onlineClientMap = new ConcurrentHashMap(); /** * 客户端与服务端连接成功 * @param session * @param username */ @OnOpen public void onOpen(Session session,@PathParam("username") String username){ /* do something for onOpen 与当前客户端连接成功时 */ onlineClientNumber.incrementAndGet();//在线数+1 onlineClientMap.put(session.getId(),session);//添加当前连接的session log.info("时间[{}]:与用户[{}]的连接成功,当前连接编号[{}],当前连接总数[{}]", new Date().toLocaleString(), username, session.getId(), onlineClientNumber); } /** * 客户端与服务端连接关闭 * @param session * @param username */ @OnClose public void onClose(Session session,@PathParam("username") String username){ /* do something for onClose 与当前客户端连接关闭时 */ onlineClientNumber.decrementAndGet();//在线数-1 onlineClientMap.remove(session.getId());//移除当前连接的session log.info("时间[{}]:与用户[{}]的连接关闭,当前连接编号[{}],当前连接总数[{}]", new Date().toLocaleString(), username, session.getId(), onlineClientNumber); } /** * 客户端与服务端连接异常 * @param error * @param session * @param username */ @OnError public void onError(Throwable error,Session session,@PathParam("username") String username) { /* do something for onError 与当前客户端连接异常时 */ error.printStackTrace(); } /** * 客户端向服务端发送消息 * @param message * @param username * @throws IOException */ @OnMessage public void onMsg(Session session,String message,@PathParam("username") String username) throws IOException { /* do something for onMessage 收到来自当前客户端的消息时 */ log.info("时间[{}]:来自连接编号为[{}]的消息:[{}]", new Date().toLocaleString(), session.getId(), message); sendAllMessage(message); } //向所有客户端发送消息(广播) private void sendAllMessage(String message){ Set sessionIdSet = onlineClientMap.keySet(); //获得Map的Key的集合 for (String sessionId : sessionIdSet) { //迭代Key集合 Session session = onlineClientMap.get(sessionId); //根据Key得到value session.getAsyncRemote().sendText(message); //发送消息给客户端 } } } 5、项目大致流程图

在这里插入图片描述

6、总结

服务端启动一个WebSocket服务,初始化应用路径、连接打开OnOpen、连接关闭OnClose、连接异常OnError、收到消息OnMessage。OnMessage中需要对来自客户端的消息进行对应的处理,比如广播或者私聊给具体某人。

客户端需要利用WebSocket对象(定义好url),然后初始化OnOpen、OnClose、OnError、OnMessage。OnMessage中需要对来自服务端的消息进行处理,如展示到页面上等。同时还可以需用WebSocket对象的send()方法来给服务端发送数据,并且切记在页面关闭时需要将该连接关闭(利用WebSocket对象的close()方法)。

7、项目地址

https://github.com/FanGaoXS/websocket-demo



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3